package org.eclipse.wst.server.preview.openejb.jetty.builder;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Properties;

import javax.ejb.EJB;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.Servlet;

import org.apache.openejb.assembler.classic.EnterpriseBeanInfo;
import org.apache.openejb.assembler.classic.JndiNameInfo;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.jetty.webapp.WebAppContext;

/**
 * EJBInjectionManager is responsible to find and inject
 * the EJB dependencies injections into servlets.
 * 
 * @author Marcelo de Oliveira Dias
 *
 */
public class EJBInjectionManager {

	private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB.createChild("jetty"), "org.eclipse.wst.server.preview.openejb.jetty");

	/**Looks for @EJB annotations in servlet classes related to a WebAppContext
	 * injects them into the EJB's dependencies injections into the servlet class. 
	 * 
	 * @param webapp to search for EJB dependencies injections.
	 */
	public void manage( WebAppContext webapp ){
	
		ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
		Thread.currentThread().setContextClassLoader( webapp.getClassLoader() );
		
		try{
			InitialContext initialContext = setupInitialContext();
			
			ServletHolder[] holders = webapp.getServletHandler().getServlets();
			for( ServletHolder holder : holders ){
				
				
				Servlet servlet = holder.getServlet();
				
				Class<? extends Servlet> servletClass = servlet.getClass();
		        Field[] declaredFields = servletClass.getDeclaredFields();
	
		        for (Field declaredField : declaredFields) {
		            Annotation[] annotations = declaredField.getAnnotations();
	
		            for (Annotation annotation : annotations) {	
		                
		            	if (EJB.class.equals(annotation.annotationType())) {
	
		                	// inject into this field
		                    Class<?> fieldType = declaredField.getType();
		                    
		                    EnterpriseBeanInfo beanInfo = getBeanFor(fieldType);
		                    if (beanInfo == null) continue;
		                   
		                    String jndiName = null;
		                    for (JndiNameInfo jndi : beanInfo.jndiNamess) {
		                    	if (jndi.intrface.equals(declaredField.getType().getName())) {
		                    		jndiName = jndi.name;
		                    	}
		                    }
		                    if (jndiName == null) continue;
		                    
		                    Object o = initialContext.lookup(jndiName);
	
		                    declaredField.setAccessible(true);
		                    declaredField.set(servlet, o);
		                }
		            }
		        }
			}
		}catch( Exception e ){			
			logger.error( "Error while building injections", e );
		}finally{
			Thread.currentThread().setContextClassLoader(oldCl);
		}
		
	}
	
	/**Find the EnterpriseBeanInfo of the Bean deployed on the server.
	 *  
	 * @param fieldType is the class wanted to be injected.
	 * @return EnterpriseBeanInfo contending the info needed to inject the Bean.
	 */
	 private EnterpriseBeanInfo getBeanFor(Class<?> fieldType) {
		 return new EJBHelper().getBeanInfo(fieldType);
	 }
	 
	 /**Configures and returns a InitialContext to look for Beans deployed on the server.
	  * The InitialContext is configured as a remote context with the default settings
	  * of the OpenEJB server url.  
	  * 
	  * @return a RemoteInitialContext configured. 
	  * @throws NamingException if the a error occur during the instantiation.
	  */
	 private InitialContext setupInitialContext() throws NamingException{
		Properties p = new Properties();
		p.put("java.naming.factory.initial", "org.apache.openejb.client.LocalInitialContextFactory");
		InitialContext initialContext = new InitialContext( p );
		return initialContext;
	 }	
	 
}
